package com.lagou.edu;

import com.lagou.edu.annotations.*;
import com.lagou.edu.factory.ProxyFactory;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLDecoder;
import java.util.*;

/**
 * @author zhangxin
 * @date 2021/4/30
 * @description TODO 此处添加class描述
 */
public class IocConfig implements ServletContextListener {

    public static HashMap<String,Object> myBeanMap = new HashMap<>();




    public static void loadClass() {
        List<Class<?>> classes = new ArrayList<>();
        try {
            Package aPackage = IocConfig.class.getPackage();
            String packageName = aPackage.getName();
            String packagePath = packageName.replace(".", "/");
            Enumeration<URL> resources = IocConfig.class.getClassLoader().getResources(packagePath);

            while (resources.hasMoreElements()){
                URL url = resources.nextElement();
                //获取包的物理路径

                String filePath = URLDecoder.decode(url.getFile(), "UTF-8");

                //以文件的方式扫描整个包下的文件 并添加到集合中

                findAndAddClassesInPackageByFile(packageName, filePath, true, classes);
                for (Class<?> aClass : classes) {
                    Service service = aClass.getAnnotation(Service.class);
                    Repository repository = aClass.getAnnotation(Repository.class);
                    Component component = aClass.getAnnotation(Component.class);
                    if(service!=null||repository!=null||component!=null){
                        Object instance = aClass.newInstance();
                        myBeanMap.put(aClass.getName(),instance);
                        Class<?>[] interfaces = aClass.getInterfaces();
                        for (Class<?> anInterface : interfaces) {
                            myBeanMap.put(anInterface.getName(),instance);
                        }
                        if(service!=null){
                            String trim = service.value().trim();
                            if (trim.length()>0) {
                                myBeanMap.put(trim,instance);
                            }
                        }
                        if(repository!=null){
                            String trim = repository.value().trim();
                            if (trim.length()>0) {
                                myBeanMap.put(trim,instance);
                            }
                        }
                        if(component!=null){
                            String trim = component.value().trim();
                            if (trim.length()>0) {
                                myBeanMap.put(trim,instance);
                            }
                        }

                    }

                }
            }

            System.out.println(myBeanMap.size());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive, List<Class<?>> classes){

        //获取此包的目录 建立一个File

        File dir = new File(packagePath);

        //如果不存在或者 也不是目录就直接返回

        if (!dir.exists() || !dir.isDirectory()) {

            return;

        }

        //如果存在 就获取包下的所有文件 包括目录

        File[] dirfiles = dir.listFiles(new FileFilter() {

            //自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)

            @Override
            public boolean accept(File file) {

                return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));

            }

        });

        //循环所有文件

        for (File file : dirfiles) {

            //如果是目录 则继续扫描

            if (file.isDirectory()) {
                String dirName = file.getName();
                /*if(dirName.contains("servlet")){
                    continue;
                }*/
                findAndAddClassesInPackageByFile(packageName + "." + file.getName(),

                        file.getAbsolutePath(),

                        recursive,

                        classes);

            }

            else {

                //如果是java类文件 去掉后面的.class 只留下类名

                String className = file.getName().substring(0, file.getName().length() - 6);

                try {
                    //添加到集合中去
                    String classForName = packageName + '.' + className;
                    classes.add(Class.forName(classForName));

                } catch (Exception e) {

                    e.printStackTrace();

                }

            }

        }

    }

    private static void setAutowired() throws IllegalAccessException {
        ProxyFactory proxyFactory = (ProxyFactory) myBeanMap.get("proxyFactory");
        Field[] proxyFactoryFields = proxyFactory.getClass().getDeclaredFields();
        for (Field field : proxyFactoryFields) {
            Autowired auto = field.getAnnotation(Autowired.class);
            if(auto!=null){
                field.setAccessible(true);
                field.set(proxyFactory,myBeanMap.get(field.getType().getName()));
            }
        }

        Set<Map.Entry<String, Object>> entrySet = myBeanMap.entrySet();
        for (Map.Entry<String, Object> entry : entrySet) {
            Object object = entry.getValue();
            if(object.getClass().equals(ProxyFactory.class)){
                continue;
            }
            Field[] fields = object.getClass().getDeclaredFields();
            for (Field field : fields) {
                Autowired auto = field.getAnnotation(Autowired.class);
                if(auto!=null){
                    field.setAccessible(true);
                    Object obj = myBeanMap.get(field.getType().getName());
                    Object proxy = checkNeedProxy(obj);
                    field.set(object,proxy);
                }
            }
        }

    }

    private static Object checkNeedProxy(Object target){
        Method[] methods = target.getClass().getDeclaredMethods();
        for (Method method : methods) {
            Transactional transactional = method.getAnnotation(Transactional.class);
            if(transactional!=null){
                ProxyFactory proxyFactory = (ProxyFactory) myBeanMap.get("proxyFactory");
                return proxyFactory.getCglibProxy(target);
            }
        }
        return target;
    }


    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        loadClass();
        try {
            setAutowired();
            System.out.println("启动完成");
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            contextDestroyed(servletContextEvent);
        }
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        myBeanMap.clear();
    }

    public static Object getBean(String beanName){
        Object obj = myBeanMap.get(beanName);
        return checkNeedProxy(obj);
    }
    public static Object getBean(Class<?> clazz){
        Object obj = myBeanMap.get(clazz.getName());
        return checkNeedProxy(obj);
    }
}
